package com.yanwq.sayhi.controller.camera;

import android.content.Context;
import android.hardware.Camera;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.Executors;

/**
 * 作者：yanweiqiang。
 * 日期：2016/3/22 0022 17:43。
 */
@SuppressWarnings("deprecation")
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
    private Camera camera;
    private int cameraId;

    private int expWidth;
    private int expHeight;
    private OnCameraStatusListener listener;

    public void setOnCameraStatusListener(OnCameraStatusListener listener) {
        this.listener = listener;
    }

    @SuppressWarnings("unused")
    public Camera getCamera() {
        return camera;
    }

    public int getCameraId() {
        return cameraId;
    }

    @SuppressWarnings("unused")
    public CameraView(Context context, int expWidth, int expHeight) {
        super(context);
        this.expWidth = 240;
        this.expHeight = 320;
        getHolder().addCallback(this);
    }

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        getHolder().addCallback(this);
    }

    public CameraView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        getHolder().addCallback(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (expWidth == 0 && expHeight == 0) {
            expWidth = MeasureSpec.getSize(widthMeasureSpec);
            expHeight = MeasureSpec.getSize(heightMeasureSpec);
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Executors.newSingleThreadExecutor().submit(new Runnable() {
            @Override
            public void run() {
                openCamera(getCameraId(Camera.CameraInfo.CAMERA_FACING_BACK));
                setPreviewSize();
                setFlashMode();
                startPreview();
                autoFocus(null);

                post(new Runnable() {
                    @Override
                    public void run() {
                        setLayoutParams();
                    }
                });
            }
        });
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        stopPreview();
        release();
    }

    private void openCamera(int cameraId) {
        this.cameraId = cameraId;
        camera = Camera.open(cameraId);
    }

    public void switchCamera() {
        stopPreview();
        release();
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(cameraId, info);

        if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
            cameraId = getCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT);
            camera = Camera.open(cameraId);
        } else {
            cameraId = getCameraId(Camera.CameraInfo.CAMERA_FACING_BACK);
            camera = Camera.open(cameraId);
        }

        setPreviewSize();
        startPreview();
        setLayoutParams();
    }

    private int getCameraId(int facing) {
        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == facing) {
                return i;
            }
        }

        return 0;
    }

    private void setPreviewSize() {
        List<Camera.Size> sizeList = camera.getParameters().getSupportedPreviewSizes();

        if (sizeList == null || sizeList.size() == 0) {
            return;
        }

        Camera.Parameters parameters = camera.getParameters();

        for (Camera.Size size : sizeList) {
            if (expWidth * expHeight == size.width * size.height) {
                parameters.setPreviewSize(size.width, size.height);
                camera.setParameters(parameters);
                return;
            }
        }

        for (Camera.Size size : sizeList) {
            float ratio = (float) expWidth / expHeight;
            float previewRatio = (float) size.height / size.width;

            if (ratio == previewRatio && expWidth * expHeight > size.width * size.height) {
                parameters.setPreviewSize(size.width, size.height);
                camera.setParameters(parameters);
                return;
            }
        }

        Camera.Size preSize = null;

        for (Camera.Size size : sizeList) {
            float ratio = (float) expWidth / expHeight;
            float previewRatio = (float) size.height / size.width;

            if (ratio == previewRatio) {
                if (preSize == null || preSize.width * preSize.height < size.width * size.height) {
                    parameters.setPreviewSize(size.width, size.height);
                    camera.setParameters(parameters);
                }
                preSize = size;
            }
        }
    }

    private void setFlashMode() {
        Camera.Parameters parameters = camera.getParameters();

        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) {
            List<String> focusModes = camera.getParameters().getSupportedFocusModes();
            if (focusModes != null) {
                Log.i("video", Build.MODEL);
                if (((Build.MODEL.startsWith("GT-I950"))
                        || (Build.MODEL.endsWith("SCH-I959"))
                        || (Build.MODEL.endsWith("MEIZU MX3"))) && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {

                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                } else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
                } else
                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
            }
        }

        camera.setParameters(parameters);
    }

    public void autoFocus(Camera.AutoFocusCallback callback) {
        camera.autoFocus(callback);
    }

    @SuppressWarnings("SuspiciousNameCombination")
    private void setLayoutParams() {
        Camera.Size size = camera.getParameters().getPreviewSize();
        getLayoutParams().width = getMeasuredWidth();
        getLayoutParams().height = (int) (getMeasuredWidth() * ((float) size.width / size.height));
        requestLayout();
    }

    public void startPreview() {
        if (camera == null) {
            return;
        }

        try {
            getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
            camera.setDisplayOrientation(90);
            camera.setPreviewDisplay(getHolder());

            post(new Runnable() {
                @Override
                public void run() {
                    if (listener != null) {
                        listener.opened(camera);
                    }
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

        camera.startPreview();
    }

    public void stopPreview() {
        if (camera == null) {
            return;
        }

        camera.stopPreview();
    }

    public void release() {
        if (camera == null) {
            return;
        }

        camera.setPreviewCallbackWithBuffer(null);
        camera.release();
        camera = null;

        post(new Runnable() {
            @Override
            public void run() {
                if (listener != null) {
                    listener.released();
                }
            }
        });
    }

    public interface OnCameraStatusListener {
        void opened(Camera camera);

        void released();
    }
}
